#include <bits/stdc++.h>
#include <pthread.h>
#include "LockGuard.hpp"
using namespace std;

template <class T>
class BLockQueue{
    queue<T> _q;
    int _capacity;
    pthread_mutex_t _mutex;
    pthread_cond_t _producer_cond;
    pthread_cond_t _consumer_cond;

    public:
        BLockQueue(int Cap = 5)
        :_capacity(Cap)
        {
            pthread_mutex_init(&_mutex, nullptr);
            pthread_cond_init(&_producer_cond, nullptr);
            pthread_cond_init(&_consumer_cond, nullptr);
        }
        bool IsFull()
        {
            return _q.size() == _capacity;
        }
        bool P_Push(const T& inData) //生产者调用的模型
        {
            {
                LockGuard local_lock(&_mutex);
                // if(IsFull())//这里用if是有bug的，如果一旦出现pthread_cond_wait的伪唤醒，那么if只有一次，那就会出现意外
                // {
                //     pthread_cond_wait(&_producer_cond, &_mutex);
                // }
                while(IsFull())
                {
                    pthread_cond_wait(&_producer_cond, &_mutex);
                }

                _q.push(inData);
                pthread_cond_signal(&_consumer_cond);
            }
            return 1;
        }
        bool P_Push(T&& inData) //生产者调用的模型
        {
            {
                LockGuard local_lock(&_mutex);
                // if(IsFull())//这里用if是有bug的，如果一旦出现pthread_cond_wait的伪唤醒，那么if只有一次，那就会出现意外
                // {
                //     pthread_cond_wait(&_producer_cond, &_mutex);
                // }
                while(IsFull())
                {
                    pthread_cond_wait(&_producer_cond, &_mutex);
                }

                _q.push(inData);
                pthread_cond_signal(&_consumer_cond);
            }
            return 1;
        }
    
        bool C_Pop(T* out) //消费者使用
        {
            {
                LockGuard local_lock(&_mutex);
                while(_q.empty())//用while不用if的原因同上
                {
                    pthread_cond_wait(&_consumer_cond, &_mutex);
                }
                *out = _q.front();
                _q.pop();
                pthread_cond_signal(&_producer_cond);
            }
            return 1;
        }
        ~BLockQueue()
        {
            pthread_mutex_destroy(&_mutex);
            pthread_cond_destroy(&_producer_cond);
            pthread_cond_destroy(&_consumer_cond);
        }
};